package services

import (
	"easyShop/core/config"
	"easyShop/core/crypto/md5"
	"easyShop/core/database/mysql"
	"easyShop/elasticsearch"
	"easyShop/model"
	"easyShop/utils"
	"errors"
	"fmt"
	"github.com/mojocn/base64Captcha"
	"gorm.io/gorm"
	"time"
)

func GetUserInfo(id int64) (model.User, string) {
	var errorMsg string
	connect, _ := GetConnectBySnowFlakeId(id)
	var user model.User
	connect.First(&user, id)
	if user.Id == 0 {
		errorMsg = "未获取用户数据"
	}

	return user, errorMsg
}

func GetUserList(page int) (mysql.Pagination, string) {
	err := ""
	connect := mysql.GetConnect().Where("id > ?", 0)

	var user []model.User
	r := mysql.Start().Page(page).Pagination(connect, user)

	if connect.Error != nil {
		err = "get user failed"
	}

	return r, err
}

func Register(username string, password string, captchaId string, captchaCode string) error {
	// 检验验证码
	var store = base64Captcha.DefaultMemStore
	if store.Verify(captchaId, captchaCode, true) == false {
		return errors.New("验证码错误")
	}

	// 检查账号是否存在
	// es检查数据是否存在，不需要查询全库数据库，全库有username唯一索引，插入重复数据会异常导出回滚
	userCount, esError := elasticsearch.GetUserCountByUsername(username)
	if esError != nil {
		return esError
	}
	if userCount != 0 {
		return errors.New("账号已被注册，请重新输入")
	}

	// 获取雪花ID和数据中心ID和工作机器ID
	snowflakeId, dataCenterId, workerId := GetSnowFlakeId()

	// 全量数据库内新增用户
	currentTime := time.Now()
	var insertUserData = model.User{
		Id:          snowflakeId,
		Username:    username,
		Password:    md5.Encode(md5.Encode(password)),
		CreatedTime: &currentTime,
		UpdatedTime: &currentTime,
		DeletedTime: nil,
	}

	var businessErr error
	// 获取全量库连接
	fullCon := mysql.GetConnect()
	// 获取分库连接
	manyCon, _ := GetConnectByDataCenterIdAndWorkerId(dataCenterId, workerId)
	// 开启事务，开启的事务都必须用事务返还的连接来操作数据库或者提交回滚操作
	fullTx := fullCon.Begin()
	manyTx := manyCon.Begin()
	var hashMold uint32
	var usernameMappingModel model.UsernameMapping
	var usernameMappingEntity interface{}
	usernameMappingNumber, _ := config.GetConfig("username_mapping_number").(uint32)
	insertUserRelData := map[string]interface{}{}
	// 全量库内新增用户
	if fullAddErr := fullTx.Create(&insertUserData); fullAddErr.Error != nil {
		businessErr = fullAddErr.Error
		goto errHandle
	}
	// 新增username和用户ID关联数据
	hashMold = utils.Hash(username)%usernameMappingNumber + 1
	usernameMappingEntity = usernameMappingModel.Table(hashMold)
	insertUserRelData["user_id"] = snowflakeId
	insertUserRelData["username"] = username
	if relErr := fullTx.Model(&usernameMappingEntity).Create(insertUserRelData); relErr.Error != nil {
		businessErr = relErr.Error
		goto errHandle
	}
	// 分库数据库内新增用户
	if manyAddErr := manyTx.Create(&insertUserData); manyAddErr.Error != nil {
		businessErr = manyAddErr.Error
		goto errHandle
	}
	// 全量es内新增用户
	if !elasticsearch.SetUserInfoToEs(insertUserData) {
		businessErr = errors.New("写入ES失败")
		goto errHandle
	}

	fullTx.Commit()
	manyTx.Commit()

errHandle:
	fullTx.Rollback()
	manyTx.Rollback()

	if businessErr != nil {
		return businessErr
	}

	return nil
}

func Login(username string, password string) (string, error) {
	// 读取根据username读取全量es的用户数据
	userInfo, esErr, userCount := elasticsearch.GetUserInfoByUsername(username)
	if esErr != nil {
		return "", esErr
	}
	// 如果es拿不到数据，则从全量数据库再拿一次
	if userCount == 0 {
		c := mysql.GetConnect()
		c.Where(model.User{Username: username}).Find(&userInfo)
		if userInfo.Id == 0 {
			return "", errors.New("用户不存在")
		}
		// 如果从全量DB拿到数据则把数据塞入全量es
		elasticsearch.SetUserInfoToEs(userInfo)
	}

	if userInfo.Password != md5.Encode(md5.Encode(password)) {
		return "", errors.New("密码错误")
	}

	encodeData := make(map[string]interface{})
	encodeData["userInfo"] = userInfo
	signString, creErr := JwtEncode(encodeData)

	if creErr != nil {
		return "", errors.New("生成令牌失败")
	}

	return signString, nil
}

func AuthLogin(userInfo *model.User) (string, error) {
	// 读取根据username读取全量es的用户数据
	var dbUser model.User
	dbUser, esErr, userCount := elasticsearch.GetUserInfoByOpenid(userInfo.Openid)
	if esErr != nil {
		return "", esErr
	}
	fullCon := mysql.GetConnect()
	// 如果es拿不到数据，则从全量数据库再拿一次
	if userCount == 0 {
		fullCon.Where("openid = ?", userInfo.Openid).Find(&dbUser)
	}

	// change user or create
	var businessErr error
	if dbUser.Id == 0 {
		// 如果没有数据则写入数据库
		// 获取雪花ID和数据中心ID和工作机器ID
		snowflakeId, dataCenterId, workerId := GetSnowFlakeId()
		userInfo.Id = snowflakeId
		var fullTx *gorm.DB
		var manyTx *gorm.DB
		var hashMold uint32
		var openidMappingModel model.OpenidMapping
		var openidMappingEntity interface{}
		openidMappingNumber, _ := config.GetConfig("openid_mapping_number").(uint32)
		insertUserRelData := map[string]interface{}{}
		// 获取分库连接
		manyCon, getManyConnectErr := GetConnectByDataCenterIdAndWorkerId(dataCenterId, workerId)
		if getManyConnectErr != nil {
			businessErr = getManyConnectErr
			goto addErrHandle
		}
		// 开启事务，开启的事务都必须用事务返还的连接来操作数据库或者提交回滚操作
		fullTx = fullCon.Begin()
		manyTx = manyCon.Begin()
		// 写入分库
		// 全量库内新增用户
		if fullAddErr := fullTx.Create(userInfo); fullAddErr.Error != nil {
			businessErr = fullAddErr.Error
			goto addErrHandle
		}
		// 新增username和用户ID关联数据
		hashMold = utils.Hash(userInfo.Openid)%openidMappingNumber + 1
		openidMappingEntity = openidMappingModel.Table(hashMold)
		insertUserRelData["user_id"] = snowflakeId
		insertUserRelData["openid"] = userInfo.Openid
		if relErr := fullTx.Model(&openidMappingEntity).Create(insertUserRelData); relErr.Error != nil {
			businessErr = relErr.Error
			goto addErrHandle
		}
		// 分库数据库内新增用户
		if manyAddErr := manyTx.Create(userInfo); manyAddErr.Error != nil {
			businessErr = manyAddErr.Error
			goto addErrHandle
		}
		// 全量es内新增用户
		if !elasticsearch.SetUserInfoToEs(*userInfo) {
			businessErr = errors.New("写入ES失败")
			goto addErrHandle
		}
		// 提交事务
		if fullTx != nil {
			fullTx.Commit()
		}

		if manyTx != nil {
			manyTx.Commit()
		}
	addErrHandle:
		// 事务回滚
		if fullTx != nil {
			fullTx.Rollback()
		}

		if manyTx != nil {
			manyTx.Rollback()
		}

	} else {
		// 如果有数据则更新数据
		// 获取分库连接
		var fullTx *gorm.DB
		var manyTx *gorm.DB
		manyCon, getManyConnectErr := GetConnectBySnowFlakeId(dbUser.Id)
		if getManyConnectErr != nil {
			businessErr = getManyConnectErr
			goto updateErrHandle
		}
		// 开启事务，开启的事务都必须用事务返还的连接来操作数据库或者提交回滚操作
		fullTx = fullCon.Begin()
		manyTx = manyCon.Begin()

		// 全量库内更新用户
		if fullUpdateErr := fullTx.Where("openid = ?", userInfo.Openid).Updates(userInfo); fullUpdateErr.Error != nil {
			businessErr = fullUpdateErr.Error
			goto updateErrHandle
		}

		// 分库数据库内更新用户
		if manyUpdateErr := manyTx.Where("openid = ?", userInfo.Openid).Updates(userInfo); manyUpdateErr.Error != nil {
			businessErr = manyUpdateErr.Error
			goto updateErrHandle
		}

		userInfo.Id = dbUser.Id

		// 全量es内修改用户
		if !elasticsearch.SetUserInfoToEs(*userInfo) {
			businessErr = errors.New("写入ES失败")
			goto updateErrHandle
		}

		// 提交事务
		if fullTx != nil {
			fullTx.Commit()
		}

		if manyTx != nil {
			manyTx.Commit()
		}

	updateErrHandle:
		// 事务回滚
		if fullTx != nil {
			fullTx.Rollback()
		}

		if manyTx != nil {
			manyTx.Rollback()
		}
	}

	if businessErr != nil {
		return "", businessErr
	}

	userInfo.Username = dbUser.Username

	// generate token
	encodeData := make(map[string]interface{})
	encodeData["userInfo"] = userInfo
	signString, creErr := JwtEncode(encodeData)

	if creErr != nil {
		return "", errors.New("生成令牌失败")
	}

	return signString, nil
}

func PerfectUserProfile(userId int64, username string, password string) error {
	con := mysql.GetConnect()
	var userInfo model.User
	userInfo.Username = username
	userInfo.Password = md5.Encode(md5.Encode(password))
	userInfo.Id = userId
	con.Where("id = ?", userId).Updates(&userInfo)

	if con.Error != nil {
		return con.Error
	}

	return nil
}

func GetAssets(userId int64) map[string]interface{} {
	con := mysql.GetConnect()
	// get user information
	var userInfo model.User
	con.Model(&model.User{}).Select("integral, balance").Where("id = ?", userId).Find(&userInfo)

	chan1 := make(chan int64)
	chan2 := make(chan int64)
	chan3 := make(chan int64)
	chan4 := make(chan int64)
	// 总订单
	go func() {
		var count int64
		con.Model(&model.Order{}).Where("user_id = ?", userId).Count(&count)
		chan1 <- count
	}()
	// 待支付订单
	go func() {
		var count int64
		con.Model(&model.Order{}).Where("user_id = ?", userId).Where("order_status = ?", 0).Where("payment_status = ?", 0).Count(&count)
		chan2 <- count
	}()
	// 待发货订单
	go func() {
		var count int64
		con.Model(&model.Order{}).Where("user_id = ?", userId).Where("order_status = ?", 1).Where("delivery_status = ?", 0).Count(&count)
		chan3 <- count
	}()
	// 待收货订单
	go func() {
		var count int64
		con.Model(&model.Order{}).Where("user_id = ?", userId).Where("order_status = ?", 1).Where("delivery_status = ?", 1).Count(&count)
		chan4 <- count
	}()

	var result = make(map[string]interface{})
	result["integral"] = userInfo.Integral
	result["balance"] = fmt.Sprintf("%.2f", userInfo.Balance)
	result["order_num"] = <-chan1
	result["padding_payment_num"] = <-chan2
	result["padding_deliver_goods_num"] = <-chan3
	result["padding_receive_num"] = <-chan4
	result["coupon_num"] = 0

	return result
}
